"""
小说正文服务
基于详细大纲生成可阅读的小说正文（章节化），并写入 novel.json 与 novel.md。
"""
import json
from typing import Optional, List
from loguru import logger

from storyforge.config import get_config, get_provider_config
from storyforge.providers import LLMProviderFactory
from storyforge.utils import (
    get_project_dir, save_json, save_text, load_json
)
from storyforge.utils.prompts import load_prompt


class NovelService:
    """小说正文生成服务"""

    def __init__(self):
        self.config = get_config()

    def generate_novel(
        self,
        project_name: str,
        provider_name: Optional[str] = None,
        prompt_set: str = "default",
        target_episodes: Optional[int] = None,
        chapters: Optional[int] = None,
        pipeline: str = "single"
    ) -> dict:
        """
        基于详细大纲生成小说正文（章节化）。

        Args:
            project_name: 项目名称
            provider_name: 提供商名称（None 则取默认）
            prompt_set: 提示词集
            target_episodes: 参考的目标集数（用于规模控制，可选）
            chapters: 期望章节数（可选）
            pipeline: 流水线模式（single|multi-role）

        Returns:
            novel_dict: { title, chapters: [{title, summary, content}], wordCount }
        """
        # 根据模式选择生成方法
        if pipeline == "multi-role":
            return self.generate_novel_multi_role(
                project_name=project_name,
                target_episodes=target_episodes,
                chapters=chapters
            )
        
        # 单模型模式（原有实现）
        project_dir = get_project_dir(project_name, self.config.paths.projects)

        # 读取详细大纲（必须）
        detailed_outline_path = project_dir / "detailed_outline.json"
        if not detailed_outline_path.exists():
            raise FileNotFoundError(f"详细大纲不存在，请先运行 outline generate: {detailed_outline_path}")

        detailed_outline = load_json(str(detailed_outline_path))

        provider_name = provider_name or self.config.defaultProvider.llm
        if not get_provider_config(provider_name):
            raise ValueError(f"提供商配置不存在: {provider_name}")

        provider = LLMProviderFactory.create_provider(provider_name)

        logger.info(
            f"开始生成小说正文，项目: {project_name}，参考集数: {target_episodes or '-'}，章节数: {chapters or '-'}"
        )

        # 提示词（system + user）
        system_prompt = load_prompt(
            role="system_novel_generator",
            context={},
            prompts_dir=self.config.paths.prompts,
            prompt_set=prompt_set
        )

        user_prompt = load_prompt(
            role="novel_generator",
            context={
                "detailed_outline": detailed_outline,
                "target_episodes": target_episodes or 0,
                "chapters": chapters or 0
            },
            prompts_dir=self.config.paths.prompts,
            prompt_set=prompt_set
        )

        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt},
        ]

        # 不限制输出tokens，让模型根据输入自行决定输出长度
        logger.info(f"调用 LLM 生成小说正文 (provider: {provider_name}, 不限制输出tokens)")

        # 要求结构化 JSON 输出
        # 使用配置中的温度，默认为 0.6 (novel)
        temp = self.config.llm.temperatures.get("novel", 0.6)
        result = provider.generate(
            messages=messages,
            temperature=temp,
            response_format={"type": "json_object"}
        )

        novel = json.loads(result) if isinstance(result, str) else result

        # 保存 JSON
        novel_json_path = project_dir / "novel.json"
        save_json(novel, str(novel_json_path))

        # 保存 Markdown
        md = self._format_novel_markdown(novel)
        novel_md_path = project_dir / "novel.md"
        save_text(md, str(novel_md_path))

        logger.info(f"小说正文已生成: {novel_json_path}")
        return novel

    def generate_novel_multi_role(
        self,
        project_name: str,
        target_episodes: Optional[int] = None,
        chapters: Optional[int] = None,
        story_bible = None
    ) -> dict:
        """
        多角色模式生成小说正文
        
        Args:
            project_name: 项目名称
            target_episodes: 参考目标集数（可选）
            chapters: 期望章节数（可选）
            story_bible: 故事事实库（可选）
            
        Returns:
            novel_dict: { title, chapters: [{title, summary, content}], wordCount }
        """
        project_dir = get_project_dir(project_name, self.config.paths.projects)

        # 读取详细大纲（必须）
        detailed_outline_path = project_dir / "detailed_outline.json"
        if not detailed_outline_path.exists():
            raise FileNotFoundError(f"详细大纲不存在，请先运行 outline generate: {detailed_outline_path}")

        detailed_outline = load_json(str(detailed_outline_path))

        logger.info(
            f"开始多角色协作生成小说正文，项目: {project_name}，参考集数: {target_episodes or '-'}，章节数: {chapters or '-'}"
        )

        # 调用多角色流水线
        from storyforge.orchestrator import MultiRolePipeline
        
        pipeline = MultiRolePipeline()
        novel = pipeline.generate_novel(
            project_name=project_name,
            detailed_outline=detailed_outline,
            target_episodes=target_episodes,
            chapters=chapters,
            story_bible=story_bible
        )

        # 保存 JSON
        novel_json_path = project_dir / "novel.json"
        save_json(novel, str(novel_json_path))

        # 保存 Markdown
        md = self._format_novel_markdown(novel)
        novel_md_path = project_dir / "novel.md"
        save_text(md, str(novel_md_path))

        logger.info(f"多角色协作完成，小说正文已生成: {novel_json_path}")
        return novel

    def _format_novel_markdown(self, novel: dict) -> str:
        """将小说 JSON 转为 Markdown 便于阅读。"""
        lines: List[str] = []
        title = novel.get("title")
        if title:
            lines.append(f"# {title}")
            lines.append("")
        
        chapters = novel.get("chapters", [])
        for i, ch in enumerate(chapters, 1):
            ch_title = ch.get("title") or f"第{i}章"
            lines.append(f"## {ch_title}")
            
            summary = ch.get("summary")
            if summary:
                lines.append(f"> {summary}")
                lines.append("")
            
            content = ch.get("content", "")
            if content:
                lines.append(content)
            else:
                lines.append("（章节内容未生成）")
            lines.append("")
        
        return "\n".join(lines)


